import com.algorithm.Asserts.Asserts;
import com.algorithm.Heap.BinaryHeap;
import com.algorithm.List.ArrayList;
import com.algorithm.List.CircleLinkedList;
import com.algorithm.List.List;
import com.algorithm.Map.HashMap;
import com.algorithm.Map.LinkedHashMap;
import com.algorithm.Map.Map;
import com.algorithm.Map.TreeMap;
import com.algorithm.Map.Map.*;
import com.algorithm.Set.HashSet;
import com.algorithm.Set.Set;
import com.algorithm.Set.TreeSet;
import com.algorithm.Stack.Stack;
import com.algorithm.Times.Times;
import com.algorithm.Tree.AVLTree;
import com.algorithm.Tree.BinarySearchTree;
import com.algorithm.Tree.RBTree;
import com.algorithm.Trie.Trie;
import com.algorithm.auxilary.Key;
import com.algorithm.auxilary.SubKey1;
import com.algorithm.auxilary.SubKey2;
import file.FileInfo;
import file.Files;

import java.util.Comparator;


public class Main {
    public static void main(String[] args) {
        testCircleLinkedList();
        testList();
        testBinarySearchTree(new AVLTree<>());
        testBinarySearchTree(new RBTree<>());
        test1();
        test2();
        test3();
        testLinkHashMap();
        testHeap();
        testTrie();
        testStack();
    }

    private static void testStack() {
        Stack<Integer> stack = new Stack<>();
        stack.push(11);
        stack.push(22);
        stack.push(33);
        stack.push(44);

        while (!stack.isEmpty()) {
            System.out.println(stack.pop());
        }
    }

    private static void testTrie() {
        Trie<Integer> trie = new Trie<>();
        trie.add("cat", 1);
        trie.add("dog", 2);
        trie.add("catalog", 3);
        trie.add("cast", 4);
        trie.add("小码哥", 5);
        Asserts.test(trie.size() == 5);
        Asserts.test(trie.startsWith("do"));
        Asserts.test(trie.startsWith("c"));
        Asserts.test(trie.startsWith("ca"));
        Asserts.test(trie.startsWith("cat"));
        Asserts.test(trie.startsWith("cata"));
        Asserts.test(!trie.startsWith("hehe"));
        Asserts.test(trie.get("小码哥") == 5);
        Asserts.test(trie.remove("cat") == 1);
        Asserts.test(trie.remove("catalog") == 3);
        Asserts.test(trie.remove("cast") == 4);
        Asserts.test(trie.size() == 2);
        Asserts.test(trie.startsWith("小"));
        Asserts.test(trie.startsWith("do"));
        Asserts.test(!trie.startsWith("c"));
        Asserts.test(trie.contains("dog"));
        Asserts.test(!trie.contains("dg"));
    }

    private static void testHeap() {
        Integer[] numbers = {81, 66, 70, 13, 72, 63, 26, 17, 71, 24, 90, 4, 37, 15, 80}; //90 81 80 72 71
        BinaryHeap<Integer> heap = new BinaryHeap<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });

        int k = 5;
        for (Integer num : numbers) {
            if (heap.size() < k) {
                heap.add(num);
            } else if (heap.get() < num) {
                heap.replace(num);
            }
        }
        heap.print();
    }


    private static void testLinkHashMap() {
        LinkedHashMap<String,Integer> linkedHashMap = new LinkedHashMap<>();
        for (int i = 0; i < 20; i++) {
            linkedHashMap.put(String.valueOf(i),i);
        }
        Asserts.test(!linkedHashMap.isEmpty());
        System.out.println(linkedHashMap);
        for (int i = 0; i < 20; i++) {
            linkedHashMap.remove("" + i);
        }
        Asserts.test(linkedHashMap.isEmpty());
        System.out.println(linkedHashMap);
    }

    static void test1Map(Map<String, Integer> map, String[] words) {
        Times.test(map.getClass().getName(), new Times.Task() {
            @Override
            public void execute() {
                for (String word : words) {
                    Integer count = map.get(word);
                    count = count == null ? 0 : count;
                    map.put(word, count + 1);
                }
                System.out.println(map.size()); // 17188

                int count = 0;
                for (String word : words) {
                    Integer i = map.get(word);
                    count += i == null ? 0 : i;
                    map.remove(word);
                }
                Asserts.test(count == words.length);
                Asserts.test(map.size() == 0);
            }
        });
    }

    private static void testBinarySearchTree(BinarySearchTree<Integer> tree) {
        Integer[] numbers = {35, 89, 44, 82, 21, 3, 6, 95, 65, 47, 30, 100, 39, 70, 91, 98, 16, 55};
        Asserts.test(tree.isEmpty());
        for (Integer number : numbers) { tree.add(number); }
        tree.print();
        Asserts.test(tree.size() == 18);
        Asserts.test(tree.contains(89));
        Asserts.test(!tree.contains(20));
        Asserts.test(!tree.isEmpty());
        tree.remove(35);
        tree.remove(44);
        Asserts.test(!tree.contains(35));
        Asserts.test(!tree.contains(44));
        tree.clear();
        Asserts.test(tree.size() == 0);
    }

    /**
     * List Test
     */
    private static void testList() {
        List<Integer> list = new ArrayList<>();
        list.add(10);
        list.add(20);
        list.add(30);
        list.add(40);
        list.add(50);
        list.add(60);
        list.add(70);
        list.add(80);
        list.add(90);
        list.add(95);
        System.out.println(list);
        Asserts.test(list.size() == 10);
        Asserts.test(!list.isEmpty());
        Asserts.test(list.get(0) == 10);
        list.remove(0);
        Asserts.test(list.size() == 9);
        Asserts.test(list.contains(50));
        Asserts.test(list.set(0,100) == 20);
        Asserts.test(list.get(0) == 100);
        list.clear();
        Asserts.test(list.isEmpty());
        Asserts.test(list.size() == 0);

        for (int i = 0; i < 100; i++) {
            list.add(i);
        }
    }

    private static void testCircleLinkedList() {
        CircleLinkedList<Integer> list = new CircleLinkedList<>();
        for (int i = 1; i <= 8; i++) {
            list.add(i);
        }

        // 指向头结点（指向1）
        list.reset();

        while (!list.isEmpty()) {
            list.next();
            list.next();
            System.out.println(list.remove());
        }
    }


    private static void test1() {
        String filepath = "E:\\Java\\src\\java\\util";
        FileInfo fileInfo = Files.read(filepath, null);
        String[] words = fileInfo.words();

        System.out.println("总行数：" + fileInfo.getLines());
        System.out.println("单词总数：" + words.length);
        System.out.println("-------------------------------------");

        test1Map(new TreeMap<>(), words);
        test1Map(new HashMap<>(), words);
    }

    /**
     * 文件数量：7711
     * 代码行数：2401298
     * 单词数量：9070480
     */
    static void test2() {
        FileInfo fileInfo = Files.read("E:\\Java\\src\\java\\util",
                new String[]{"java"});

        System.out.println("文件数量：" + fileInfo.getFiles());
        System.out.println("代码行数：" + fileInfo.getLines());
        String[] words = fileInfo.words();
        System.out.println("单词数量：" + words.length);

        Map<String, Integer> map = new HashMap<>();
        for (String word : words) {
            Integer count = map.get(word);
            count = (count == null) ? 1 : (count + 1);
            map.put(word, count);
        }

    }

    static void test2(HashMap<Object, Integer> map) {
        for (int i = 1; i <= 20; i++) {
            map.put(new Key(i), i);
        }
        for (int i = 5; i <= 7; i++) {
            map.put(new Key(i), i + 5);
        }
        Asserts.test(map.size() == 20);
        Asserts.test(map.get(new Key(4)) == 4);
        Asserts.test(map.get(new Key(5)) == 10);
        Asserts.test(map.get(new Key(6)) == 11);
        Asserts.test(map.get(new Key(7)) == 12);
        Asserts.test(map.get(new Key(8)) == 8);
    }

    static void test3(HashMap<Object, Integer> map) {
        map.put(null, 1); // 1
        map.put(new Object(), 2); // 2
        map.put("jack", 3); // 3
        map.put(10, 4); // 4
        map.put(new Object(), 5); // 5
        map.put("jack", 6);
        map.put(10, 7);
        map.put(null, 8);
        map.put(10, null);
        Asserts.test(map.size() == 5);
        Asserts.test(map.get(null) == 8);
        Asserts.test(map.get("jack") == 6);
        Asserts.test(map.get(10) == null);
        Asserts.test(map.get(new Object()) == null);
        Asserts.test(map.containsKey(10));
        Asserts.test(map.containsKey(null));
        Asserts.test(map.containsValue(null));
        Asserts.test(map.containsValue(1) == false);
    }

    static void test4(HashMap<Object, Integer> map) {
        map.put("jack", 1);
        map.put("rose", 2);
        map.put("jim", 3);
        map.put("jake", 4);
        map.remove("jack");
        map.remove("jim");
        for (int i = 1; i <= 10; i++) {
            map.put("test" + i, i);
            map.put(new Key(i), i);
        }
        for (int i = 5; i <= 7; i++) {
            Asserts.test(map.remove(new Key(i)) == i);
        }
        for (int i = 1; i <= 3; i++) {
            map.put(new Key(i), i + 5);
        }
        Asserts.test(map.size() == 19);
        Asserts.test(map.get(new Key(1)) == 6);
        Asserts.test(map.get(new Key(2)) == 7);
        Asserts.test(map.get(new Key(3)) == 8);
        Asserts.test(map.get(new Key(4)) == 4);
        Asserts.test(map.get(new Key(5)) == null);
        Asserts.test(map.get(new Key(6)) == null);
        Asserts.test(map.get(new Key(7)) == null);
        Asserts.test(map.get(new Key(8)) == 8);
        map.traversal(new Visitor<Object, Integer>() {
            public boolean visit(Object key, Integer value) {
                System.out.println(key + "_" + value);
                return false;
            }
        });
    }

    static void test5(HashMap<Object, Integer> map) {
        for (int i = 1; i <= 20; i++) {
            map.put(new SubKey1(i), i);
        }
        map.put(new SubKey2(1), 5);
        Asserts.test(map.get(new SubKey1(1)) == 5);
        Asserts.test(map.get(new SubKey2(1)) == 5);
        Asserts.test(map.size() == 20);
    }


    static void test3() {
        Set<String> set = new TreeSet<>();
        set.add("c");
        set.add("b");
        set.add("c");
        set.add("c");
        set.add("a");

        set.traversal(new Set.Visitor<String>() {
            public boolean visit(String element) {
                System.out.println(element);
                return false;
            }
        });

        Set<String> hashSet = new HashSet<>();
        set.add("c");
        set.add("b");
        set.add("c");
        set.add("c");
        set.add("a");

        set.traversal(new Set.Visitor<String>() {
            public boolean visit(String element) {
                System.out.println(element);
                return false;
            }
        });
    }
}
